/*******************************************************************************
 * Copyright (c) 2020-10-29 @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>. All rights
 * reserved.
 *
 * Contributors: <a href="mailto:iffiff1@gmail.com">Tyler Chen</a> - initial API and implementation.
 ******************************************************************************/
package org.iff.util;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.BeanUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * Utils
 *
 * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
 * @since 2020-10-29
 */
public class Utils {
    // ===============================================reflect============================================================

    /**
     * 获得字段的值
     */
    public static Object getValue(Object data, String fieldName) {
        try {
            if (data == null) {
                return null;
            }
            PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(data.getClass(), fieldName);
            if (pd == null) {
                return null;
            }
            return pd.getReadMethod().invoke(data, new Object[0]);
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * 设置字段的值
     */
    public static boolean setValue(Object entity, String fieldName, Object value) {
        try {
            PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(entity.getClass(), fieldName);
            pd.getWriteMethod().invoke(entity, value);
            return true;
        } catch (Exception e) {
        }
        return false;
    }

    // ===============================================numberic==============================================================
    //===============================================Math===============================================================
    public static boolean isZeroOrNull(Number n1) {
        if (n1 == null) {
            return true;
        }
        if (n1 instanceof BigDecimal) {
            return BigDecimal.ZERO.compareTo((BigDecimal) n1) == 0;
        }
        return n1.doubleValue() == 0;
    }

    public static BigDecimal toBigDecimal(Number number) {
        return number == null ? BigDecimal.ZERO
                : (number instanceof BigDecimal ? (BigDecimal) number
                : BigDecimal.valueOf(number.doubleValue()));
    }

    public static BigDecimal toBigDecimal(Number number, int scale) {
        BigDecimal value = number == null ? BigDecimal.ZERO
                : (number instanceof BigDecimal ? (BigDecimal) number
                : BigDecimal.valueOf(number.doubleValue()));
        return value.setScale(scale, RoundingMode.HALF_UP);
    }

    public static BigDecimal add(Number... values) {
        BigDecimal total = BigDecimal.ZERO;
        if (values == null || values.length < 1) {
            return total;
        }
        for (Number value : values) {
            total = total.add(toBigDecimal(value));
        }
        return total;
    }

    public static BigDecimal subtract(Number a, Number b) {
        return toBigDecimal(a).subtract(toBigDecimal(b));
    }

    public static BigDecimal multiply(Number a, Number b) {
        return toBigDecimal(a).multiply(toBigDecimal(b));
    }

    public static BigDecimal multiply(Number a, Number b, int scale) {
        return toBigDecimal(a, scale).multiply(toBigDecimal(b, scale)).setScale(4, RoundingMode.HALF_UP);
    }

    public static BigDecimal divide(Number a, Number b) {
        return divide(a, b, 2);
    }

    public static BigDecimal divide(Number a, Number b, int scale) {
        BigDecimal bb = toBigDecimal(b);
        return bb.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : toBigDecimal(a).divide(bb, scale, RoundingMode.HALF_UP);
    }

    public static BigDecimal divide(Number a, Number b, int scale, RoundingMode mode) {
        BigDecimal bb = toBigDecimal(b);
        return bb.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : toBigDecimal(a).divide(bb, scale, mode == null ? RoundingMode.HALF_UP : mode);
    }

    public static BigDecimal sum(Collection<Number> amounts) {
        if (amounts == null || amounts.isEmpty()) {
            return BigDecimal.ZERO;
        }
        BigDecimal total = BigDecimal.ZERO;
        for (Number amount : amounts) {
            total = add(total, amount);
        }
        return total;
    }

    public static <T> BigDecimal sum(Collection<T> list, Function<T, Number> value) {
        if (list == null || list.isEmpty()) {
            return BigDecimal.ZERO;
        }
        BigDecimal[] total = new BigDecimal[]{BigDecimal.ZERO};
        list.forEach(t -> total[0] = add(total[0], value.apply(t)));
        return total[0];
    }

    public static <T> BigDecimal avg(List<T> list, Function<T, Number> value) {
        BigDecimal[] total = new BigDecimal[]{BigDecimal.ZERO};
        nullToEmptyList(list).forEach(t -> total[0] = add(total[0], value.apply(t)));
        return nullToEmptyList(list).size() > 0 ? divide(total[0], list.size()) : toBigDecimal(0);
    }
    // ===============================================date==============================================================

    /**
     * 返回指定月份的起始及结束时间
     *
     * @param date
     * @return left：月份起始时间，right：月份结束时间
     */
    public static Pair<Date, Date> monthStartAndEnd(Date date) {
        return monthStartAndEnd(date, 0);
    }

    /**
     * 返回指定月份+月份增量的起始及结束时间
     *
     * @param date
     * @return left：月份起始时间，right：月份结束时间
     */
    public static Pair<Date, Date> monthStartAndEnd(Date date, int addMonth) {
        Calendar cal = cleanCalendar(date, Calendar.MONTH);
        cal.add(Calendar.MONTH, addMonth);
        Date start = cal.getTime();
        cal.add(Calendar.MONTH, 1);
        cal.add(Calendar.MILLISECOND, -1);
        Date end = cal.getTime();
        return Pair.of(start, end);
    }

    /**
     * 返回指定日期的起始及结束时间
     *
     * @param date
     * @return left：日期起始时间，right：日期结束时间
     */
    public static Pair<Date, Date> dayStartAndEnd(Date date) {
        return dayStartAndEnd(date, 0);
    }

    /**
     * 返回指定日期的起始及结束时间
     *
     * @param date
     * @return left：日期起始时间，right：日期结束时间
     */
    public static Pair<Date, Date> dayStartAndEnd(Date date, int addDay) {
        Calendar cal = cleanCalendar(date, Calendar.DAY_OF_MONTH);
        cal.add(Calendar.DAY_OF_MONTH, addDay);
        Date start = cal.getTime();
        cal.add(Calendar.DAY_OF_MONTH, 1);
        cal.add(Calendar.MILLISECOND, -1);
        Date end = cal.getTime();
        return Pair.of(start, end);
    }

    /**
     * 返回指定周的起始及结束时间
     *
     * @param date
     * @return left：周起始时间，right：周结束时间
     */
    public static Pair<Date, Date> weekStartAndEnd(Date date) {
        return weekStartAndEnd(date, 0);
    }

    /**
     * 返回指定周的起始及结束时间。
     *
     * @param date
     * @return left：周起始时间，right：周结束时间
     */
    public static Pair<Date, Date> weekStartAndEnd(Date date, int addWeek) {
        Calendar cal = cleanCalendar(date, Calendar.DAY_OF_MONTH);
        cal.add(Calendar.WEEK_OF_YEAR, addWeek);
        cal.add(Calendar.DAY_OF_WEEK, Calendar.SUNDAY - cal.get(Calendar.DAY_OF_WEEK));
        Date start = cal.getTime();
        cal.add(Calendar.DAY_OF_WEEK, Calendar.SATURDAY - cal.get(Calendar.DAY_OF_WEEK));
        cal.add(Calendar.DAY_OF_MONTH, 1);
        cal.add(Calendar.MILLISECOND, -1);
        Date end = cal.getTime();
        return Pair.of(start, end);
    }

    /**
     * 返回指定年份的起始及结束时间
     *
     * @param date
     * @return left：年份起始时间，right：年份结束时间
     */
    public static Pair<Date, Date> yearStartAndEnd(Date date) {
        return yearStartAndEnd(date, 0);
    }

    /**
     * 返回指定年份的起始及结束时间
     *
     * @param date
     * @return left：年份起始时间，right：年份结束时间
     */
    public static Pair<Date, Date> yearStartAndEnd(Date date, int addYear) {
        Calendar cal = cleanCalendar(date, Calendar.YEAR);
        cal.add(Calendar.YEAR, addYear);
        Date start = cal.getTime();
        cal.add(Calendar.YEAR, 1);
        cal.add(Calendar.MILLISECOND, -1);
        Date end = cal.getTime();
        return Pair.of(start, end);
    }

    public static Calendar cleanCalendar(Date date, int fieldType) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cleanCalendar(cal, fieldType);
    }

    /**
     * 把小于等于 fieldType 的值全部清零
     *
     * @param fieldType
     * @return
     */
    public static Calendar cleanCalendar(Calendar cal, int fieldType) {
        cal.setLenient(true);
        switch (fieldType) {
            case Calendar.YEAR:
                cal.set(Calendar.MONTH, Calendar.JANUARY);
            case Calendar.MONTH:
                cal.set(Calendar.DAY_OF_MONTH, 1);
            case Calendar.DAY_OF_MONTH:
                cal.set(Calendar.HOUR_OF_DAY, 0);
            case Calendar.HOUR_OF_DAY:
                cal.set(Calendar.MINUTE, 0);
            case Calendar.MINUTE:
                cal.set(Calendar.SECOND, 0);
            case Calendar.SECOND:
                cal.set(Calendar.MILLISECOND, 0);
            case Calendar.MILLISECOND:
            default:
        }
        return cal;
    }

    // ===============================================Util==============================================================

    public static <T> List<T> nullToEmptyList(List<T> list) {
        return list == null ? new ArrayList<>() : list;
    }

    public static <T> Set<T> nullToEmptySet(Set<T> set) {
        return set == null ? new HashSet<>() : set;
    }

    public static <T> Collection<T> nullToEmptyCollection(Collection<T> list) {
        return list == null ? new ArrayList<>() : list;
    }

    public static <K, V> Map<K, V> nullToEmptyMap(Map<K, V> map) {
        return map == null ? Collections.emptyMap() : map;
    }

    public static <T, R> List<R> collect(Collection<T> list, Function<T, R> function) {
        return nullToEmptyCollection(list).stream().map(function).collect(Collectors.toList());
    }

    public static <T, R> List<R> collect(Collection<T> list, Function<T, R> function, Predicate<R> predicate) {
        return nullToEmptyCollection(list).stream().map(function).filter(r -> predicate.test(r)).collect(Collectors.toList());
    }

    public static <T, R> Set<R> collectSet(Collection<T> list, Function<T, R> function) {
        return nullToEmptyCollection(list).stream().map(function).collect(Collectors.toSet());
    }

    public static <T, R> Set<R> collectSet(Collection<T> list, Function<T, R> function, Predicate<R> predicate) {
        return nullToEmptyCollection(list).stream().map(function).filter(r -> predicate.test(r)).collect(Collectors.toSet());
    }

    public static <T, R> List<R> collectNotNull(Collection<T> list, Function<T, R> function) {
        return nullToEmptyCollection(list).stream().map(function).filter(item -> item != null).collect(Collectors.toList());
    }

    public static <T, R> Set<R> collectNotNullSet(Collection<T> list, Function<T, R> function) {
        return nullToEmptyCollection(list).stream().map(function).filter(item -> item != null).collect(Collectors.toSet());
    }

    public static <T, R> Map<R, T> collectMap(Collection<T> list, Function<T, R> function) {
        return nullToEmptyCollection(list).stream()
                .collect(Collectors.toMap(function, v -> v, (v1, v2) -> v2, LinkedHashMap::new));
    }

    public static <T, R> Map<R, T> collectMap(Collection<T> list, Function<T, R> function, Predicate<T> predicate) {
        return nullToEmptyCollection(list).stream().filter(e -> predicate.test(e))
                .collect(Collectors.toMap(function, v -> v, (v1, v2) -> v2, LinkedHashMap::new));
    }

    public static <T, R> Map<R, List<T>> collectMapList(Collection<T> list, Function<T, R> function) {
        return nullToEmptyCollection(list).stream()
                .collect(Collectors.groupingBy(function, LinkedHashMap::new, Collectors.toList()));
    }

    public static <T> List<T> filter(Collection<T> list, Predicate<T> predicate) {
        return nullToEmptyCollection(list).stream().filter(r -> predicate.test(r)).collect(Collectors.toList());
    }

    public static <T> T first(Collection<T> list, Predicate<T> predicate) {
        return nullToEmptyCollection(list).stream().filter(r -> predicate.test(r)).findFirst().orElse(null);
    }

    public static <T> boolean contains(Collection<T> list, Predicate<T> predicate) {
        return nullToEmptyCollection(list).stream().filter(r -> predicate.test(r)).findFirst().isPresent();
    }

    public static <T> String join(Collection<T> list, Function<T, String> function) {
        return nullToEmptyCollection(list).stream().map(function).collect(Collectors.joining(","));
    }

    public static <T> String joinNotBlank(Collection<T> list, Function<T, String> function) {
        return nullToEmptyCollection(list).stream().map(function).filter(s -> StringUtils.isNotBlank(s))
                .collect(Collectors.joining(","));
    }

    public static <T> String join(Collection<T> list, Function<T, String> function, Predicate<String> predicate) {
        return nullToEmptyCollection(list).stream().map(function).filter(predicate).collect(Collectors.joining(","));
    }

    public static <T> void forEach(Collection<T> list, Consumer<T> function) {
        nullToEmptyCollection(list).forEach(function);
    }

    public static <T> void forEach(Iterator<T> list, Consumer<T> function) {
        if (list == null) {
            return;
        }
        while (list.hasNext()) {
            function.accept(list.next());
        }
    }

    public static <T, R> Optional<R> mapPresent(T test, Function<T, R> func) {
        if (test != null && func != null) {
            return Optional.ofNullable(func.apply(test));
        }
        return Optional.empty();
    }

    public static <T> Optional<T> mapNull(Object test, Supplier<T> supplier) {
        if (test == null && supplier != null) {
            return Optional.ofNullable(supplier.get());
        }
        return Optional.empty();
    }

    public static <T> Optional<T> mapBlank(Object test, Supplier<T> supplier) {
        if (isNullOrBlank(test) && supplier != null) {
            return Optional.ofNullable(supplier.get());
        }
        return Optional.empty();
    }

    public static boolean isNullOrBlank(Object obj) {
        if (obj == null) {
            return true;
        }
        if (obj instanceof CharSequence) {
            return StringUtils.isBlank((CharSequence) obj);
        }
        if (obj instanceof Collection) {
            return ((Collection) obj).isEmpty();
        }
        if (obj instanceof Map) {
            return ((Map) obj).isEmpty();
        }
        return false;
    }

    public static boolean isNotNullOrBlank(Object obj) {
        return !isNullOrBlank(obj);
    }

    public static boolean isEmpty(Object obj) {
        if (obj == null) {
            return true;
        }
        if (obj instanceof CharSequence) {
            return StringUtils.isBlank((CharSequence) obj);
        } else if (obj instanceof Collection) {
            return ((Collection<?>) obj).size() < 1;
        } else if (obj instanceof Map) {
            return ((Map<?, ?>) obj).size() < 1;
        } else if (obj.getClass().isArray()) {
            return Array.getLength(obj) < 1;
        }
        return false;
    }

    public static boolean allEmpty(Object... objs) {
        if (objs == null || objs.length < 1) {
            return true;
        }
        for (Object obj : objs) {
            if (obj == null) {
                continue;
            }
            if (obj instanceof CharSequence) {
                if (!StringUtils.isBlank((CharSequence) obj)) {
                    return false;
                }
            } else if (obj instanceof Collection) {
                if (((Collection<?>) obj).size() > 0) {
                    return false;
                }
            } else if (obj instanceof Map) {
                if (((Map<?, ?>) obj).size() > 0) {
                    return false;
                }
            } else if (obj.getClass().isArray()) {
                if (Array.getLength(obj) > 0) {
                    return false;
                }
            }
        }
        return true;
    }

    public static boolean anyEmpty(Object... objs) {
        if (objs == null || objs.length < 1) {
            return true;
        }
        for (Object obj : objs) {
            if (obj == null) {
                return true;
            }
            if (obj instanceof CharSequence) {
                if (StringUtils.isBlank((CharSequence) obj)) {
                    return true;
                }
            } else if (obj instanceof Collection) {
                if (((Collection<?>) obj).size() < 1) {
                    return true;
                }
            } else if (obj instanceof Map) {
                if (((Map<?, ?>) obj).size() < 1) {
                    return true;
                }
            } else if (obj.getClass().isArray()) {
                if (Array.getLength(obj) < 1) {
                    return true;
                }
            }
        }
        return false;
    }

    public static <T> T copy(Object src, T target) {
        if (src == null) {
            return target;
        }
        if (target == null) {
            return null;
        }
        BeanUtils.copyProperties(src, target);
        return target;
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public static Map<?, ?> toMap(Object... keyValues) {
        Map map = new LinkedHashMap();
        if (keyValues == null || keyValues.length < 1 || keyValues.length % 2 != 0) {
            return map;
        }
        for (int i = 0; i < keyValues.length; i++) {
            map.put(keyValues[i], keyValues[i + 1]);
            i = i + 1;
        }
        return map;
    }

    @SuppressWarnings({"unchecked"})
    public static <T> List<T> toList(T... values) {
        List<T> list = new ArrayList<>();
        if (values == null || values.length < 1) {
            return list;
        }
        for (T value : values) {
            list.add(value);
        }
        return list;
    }

    @SuppressWarnings({"unchecked"})
    public static <T> List<T> toListIgnoreNull(T... values) {
        List<T> list = new ArrayList<>();
        if (values == null || values.length < 1) {
            return list;
        }
        for (T value : values) {
            if (value != null) {
                list.add(value);
            }
        }
        return list;
    }


    /**
     * 把对象的null值删除。
     *
     * <pre>
     * 输入值        : 返回值
     * =============:======
     * null         : null
     * CharSequence : str
     * =============:======
     * </pre>
     *
     * @param value 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static String removeNull(String value) {
        if (value == null) {
            return null;
        }
        return value;
    }

    /**
     * 把对象的null值删除。
     *
     * <pre>
     * 如果当前 Collection 可以被修改就直接修改，否则返回一个新的 Collection 。
     * 输入值        : 返回值
     * =============:======
     * null         : null
     * Collection   : remove null value, [null, 1, " a ", " "] => [1, " a ", " "]
     * =============:======
     * </pre>
     *
     * @param object 对象
     * @return 对象，仅返回ArrayList, SortedSet, Set
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    @SuppressWarnings("rawtypes")
    public static <T> Collection<T> removeNull(Collection<T> object) {
        if (object == null || object.isEmpty()) {
            return object;
        }
        try {// 如果当前 Collection 可以被修改就直接修改，否则返回一个新的 Collection 。
            Object[] array = object.toArray();
            object.clear();
            for (Object obj : array) {
                if (obj != null) {
                    object.add((T) obj);
                }
            }
            return object;
        } catch (Exception e) {
            Collection clt = new ArrayList<T>();
            if (object instanceof SortedSet) {
                clt = new TreeSet<T>();
            } else if (object instanceof Set) {
                clt = new HashSet<T>();
            }
            Object[] array = ((Collection) object).toArray();
            for (Object obj : array) {
                if (obj != null) {
                    clt.add((T) obj);
                }
            }
            return clt;
        }
    }

    /**
     * 把对象的null值删除。
     *
     * <pre>
     * 如果当前 Map 可以被修改就直接修改，否则返回一个新的 Map 。
     * 输入值        : 返回值
     * =============:======
     * null         : null
     * Map          : remove null value, {"1": null, "2": " a ", " ": " "} => {"2": " a ", " ": " "}
     * =============:======
     * </pre>
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    @SuppressWarnings("rawtypes")
    public static Map<?, ?> removeNull(Map<?, ?> object) {
        if (object == null || object.isEmpty()) {
            return object;
        }
        try {// 如果当前 Map 可以被修改就直接修改，否则返回一个新的 Map 。
            Object[] array = object.keySet().toArray();
            for (Object key : array) {
                if (key == null || object.get(key) == null) {
                    object.remove(key);
                }
            }
            return object;
        } catch (Exception e) {
            Map result = new HashMap();
            if (object instanceof SortedMap) {
                result = new TreeMap();
            } else if (object instanceof LinkedHashMap) {
                result = new LinkedHashMap();
            }
            Object[] array = object.keySet().toArray();
            for (Object key : array) {
                if (key == null || object.get(key) == null) {
                    continue;
                }
                result.put(key, object.get(key));
            }
            return result;
        }
    }

    /**
     * 把对象的null值删除。
     *
     * <pre>
     * 输入值        : 返回值
     * =============:======
     * null         : null
     * Array        : remove null value, [null, 1, " a ", " "] => [1, " a ", " "]
     * =============:======
     * </pre>
     *
     * @param object 对象
     * @param <T>    对象的类型
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static <T> T[] removeNull(T[] object) {
        if (object == null || object.length == 0) {
            return object;
        }
        int len = Array.getLength(object);
        int count = 0;
        for (int i = 0; i < len; i++) {
            count = count + (Array.get(object, i) == null ? 0 : 1);
        }
        Object newInstance = Array.newInstance(object.getClass().getComponentType(), count);
        int pos = 0;
        for (int i = 0; i < len; i++) {
            Object value = Array.get(object, i);
            if (value != null) {
                Array.set(newInstance, pos, value);
                pos++;
            }
        }
        return (T[]) newInstance;
    }

    /**
     * 把对象的null值及空值删除。
     *
     * <pre>
     * 输入值        : 返回值
     * =============:======
     * null         : null
     * String : StringUtils.isBlank(object) ? "" : object
     * =============:======
     * </pre>
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static String removeBlank(String object) {
        return object == null ? object : (StringUtils.isBlank(object) ? "" : object);
    }

    /**
     * 把对象的null值及空值删除。
     *
     * <pre>
     * 如果当前 Collection 可以被修改就直接修改，否则返回一个新的 Collection 。
     * 输入值        : 返回值
     * =============:======
     * null         : null
     * Collection   : remove null value and blank value, [null, 1, " a ", " "] => [1, " a "]
     * =============:======
     * </pre>
     *
     * @param object 对象
     * @param <T>    对象的类型
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    @SuppressWarnings("rawtypes")
    public static <T> Collection<T> removeBlank(Collection<T> object) {
        if (object == null || object.isEmpty()) {
            return object;
        }
        try {// 如果当前 Collection 可以被修改就直接修改，否则返回一个新的 Collection 。
            Object[] array = object.toArray();
            object.clear();
            for (Object obj : array) {
                if (blankToNull(obj) != null) {
                    object.add((T) obj);
                }
            }
            return object;
        } catch (Exception e) {
            Collection clt = new ArrayList<T>();
            if (object instanceof SortedSet) {
                clt = new TreeSet<T>();
            } else if (object instanceof Set) {
                clt = new HashSet<T>();
            }
            Object[] array = ((Collection) object).toArray();
            for (Object obj : array) {
                if (blankToNull(obj) != null) {
                    clt.add((T) obj);
                }
            }
            return clt;
        }
    }

    /**
     * 把对象的null值及空值删除。
     *
     * <pre>
     * 输入值        : 返回值
     * =============:======
     * null         : null
     * Map          : remove null value and blank value, {"1": null, "2": " a ", " ": " "} => {"2": " a "}
     * =============:======
     * </pre>
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    @SuppressWarnings("rawtypes")
    public static Map<?, ?> removeBlank(Map<?, ?> object) {
        if (object == null || object.isEmpty()) {
            return object;
        }
        try {// 如果当前 Map 可以被修改就直接修改，否则返回一个新的 Map 。
            Object[] array = object.keySet().toArray();
            for (Object key : array) {
                Object key1 = blankToNull(key);
                Object value = object.get(key);
                value = blankToNull(value);
                if (key1 == null || value == null) {
                    object.remove(key);
                }
            }
            return object;
        } catch (Exception e) {
            Map result = new HashMap();
            if (object instanceof SortedMap) {
                result = new TreeMap();
            } else if (object instanceof LinkedHashMap) {
                result = new LinkedHashMap();
            }
            Object[] array = object.keySet().toArray();
            for (Object key : array) {
                Object key1 = blankToNull(key);
                Object value = object.get(key);
                value = blankToNull(value);
                if (key1 == null || value == null) {
                    continue;
                }
                result.put(key, object.get(key));
            }
            return result;
        }
    }

    /**
     * 把对象的null值及空值删除。
     *
     * <pre>
     * 输入值        : 返回值
     * =============:======
     * null         : null
     * Array        : remove null value and blank value, [null, 1, " a ", " "] => [1, " a "]
     * =============:======
     * </pre>
     *
     * @param object 对象
     * @param <T>    对象的类型
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static <T> T[] removeBlank(T[] object) {
        if (object == null || object.length < 1) {
            return object;
        }
        int len = Array.getLength(object);
        Object newInstance = Array.newInstance(object.getClass().getComponentType(), len);
        int count = 0;
        for (int i = 0; i < len; i++) {
            Object obj = blankToNull(Array.get(object, i));
            if (obj != null) {
                Array.set(newInstance, count, obj);
                count++;
            }
        }
        return (T[]) ArrayUtils.subarray((T[]) newInstance, 0, count);
    }

    private static Object blankToNull(Object obj) {
        return (obj instanceof CharSequence) && StringUtils.isBlank((CharSequence) obj) ? null : obj;
    }

    /**
     * 把对象的null值及Blank值删除，并把字符串进行trim。
     *
     * <pre>
     * 输入值        : 返回值
     * =============:======
     * null         : null
     * String : blank=null or str.trim()
     * =============:======
     * </pre>
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static String trimAndRemoveBlank(String object) {
        if (object == null || object.length() < 1) {
            return object;
        }
        return StringUtils.trim(object);
    }

    /**
     * 把对象的null值及Blank值删除，并把字符串进行trim。
     *
     * <pre>
     * 输入值        : 返回值
     * =============:======
     * null         : null
     * Collection   : remove null value and trim string value, [null, 1, " a ", ""] => [1, "a"]
     * =============:======
     * </pre>
     *
     * @param object 对象
     * @param <T>    对象的类型
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @date 2019-02-25
     * @since Mar 4, 2018
     */
    @SuppressWarnings("rawtypes")
    public static <T> Collection<T> trimAndRemoveBlank(Collection<T> object) {
        if (object == null || object.isEmpty()) {
            return object;
        }
        try {// 如果当前 Collection 可以被修改就直接修改，否则返回一个新的 Collection 。
            Object[] array = object.toArray();
            object.clear();
            for (Object obj : array) {
                obj = trim(obj);
                if (obj != null) {
                    object.add((T) obj);
                }
            }
            return object;
        } catch (Exception e) {
            Collection clt = new ArrayList<T>();
            if (object instanceof SortedSet) {
                clt = new TreeSet<T>();
            } else if (object instanceof Set) {
                clt = new HashSet<T>();
            }
            Object[] array = ((Collection) object).toArray();
            for (Object obj : array) {
                obj = trim(obj);
                if (obj != null) {
                    clt.add((T) obj);
                }
            }
            return clt;
        }
    }

    private static Object trim(Object obj) {
        if (obj instanceof String) {
            String trim = StringUtils.trim((String) obj);
            return trim.length() < 1 ? null : trim;
        }
        if (obj instanceof StringBuffer) {
            String str = obj.toString();
            String trim = StringUtils.trim(str);
            return trim.length() == str.length() ? obj : (trim.length() < 1 ? null : new StringBuilder(trim));
        }
        if (obj instanceof StringBuilder) {
            String str = obj.toString();
            String trim = StringUtils.trim(str);
            return trim.length() == str.length() ? obj : (trim.length() < 1 ? null : new StringBuilder(trim));
        }
        if (obj instanceof CharSequence) {
            return StringUtils.isBlank(((CharSequence) obj)) ? null : obj;
        }
        return obj;
    }

    /**
     * 把对象的null值及Blank值删除，并把字符串进行trim。
     *
     * <pre>
     * 输入值        : 返回值
     * =============:======
     * null         : null
     * Map          : remove null value and trim string value, {"1": null, " 2 ": " a ", "3": ""} => {"2": "a"}
     * =============:======
     * </pre>
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @date 2019-02-25
     * @since Mar 4, 2018
     */
    @SuppressWarnings("rawtypes")
    public static Map<?, ?> trimAndRemoveBlank(Map<?, ?> object) {
        if (object == null || object.isEmpty()) {
            return object;
        }
        try {// 如果当前 Map 可以被修改就直接修改，否则返回一个新的 Map 。
            Object[] array = object.keySet().toArray();
            for (Object key : array) {
                Object key1 = trim(key);
                Object value = trim(object.get(key));
                if (key1 == null || value == null) {
                    object.remove(key);
                }
            }
            return object;
        } catch (Exception e) {
            Map result = new HashMap();
            if (object instanceof SortedMap) {
                result = new TreeMap();
            } else if (object instanceof LinkedHashMap) {
                result = new LinkedHashMap();
            }
            Object[] array = object.keySet().toArray();
            for (Object key : array) {
                Object key1 = trim(key);
                Object value = trim(object.get(key));
                if (key1 == null || value == null) {
                    continue;
                }
                result.put(key, object.get(key));
            }
            return result;
        }
    }

    /**
     * 把对象的null值及Blank值删除，并把字符串进行trim。
     *
     * <pre>
     * 输入值        : 返回值
     * =============:======
     * null         : null
     * Array        : remove null value and trim string value, [null, 1, " a ", ""] => [1, "a"]
     * =============:======
     * </pre>
     *
     * @param object 对象
     * @param <T>    对象的类型
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @date 2019-02-25
     * @since Mar 4, 2018
     */
    public static <T> T[] trimAndRemoveBlank(T[] object) {
        if (object == null || object.length < 1) {
            return object;
        }
        int len = Array.getLength(object);
        Object newInstance = Array.newInstance(object.getClass().getComponentType(), len);
        int count = 0;
        for (int i = 0; i < len; i++) {
            Object obj = trim(Array.get(object, i));
            if (obj != null) {
                Array.set(newInstance, count, obj);
                count++;
            }
        }
        return (T[]) ArrayUtils.subarray((T[]) newInstance, 0, count);
    }

    public static List<String> splitToList(String source, String split) {
        List<String> list = new ArrayList<>();
        if (StringUtils.isBlank(source)) {
            return list;
        }
        String[] array = StringUtils.split(source, split);
        for (String s : array) {
            s = s.trim();
            if (s.length() > 0) {
                list.add(s);
            }
        }
        return list;
    }

    /**
     * Safe get without exception
     */
    public static <T> T safeGet(List<T> list, int index) {
        if (list == null || list.size() <= index) {
            return null;
        }
        return list.get(index);
    }

    /**
     * Safe get without exception
     */
    public static <T> T safeGet(T[] array, int index) {
        if (array == null || array.length <= index) {
            return null;
        }
        return array[index];
    }

    /**
     * 如果为null，则返回空字符串。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static String nullToEmpty(String object) {
        return object == null ? "" : object;
    }

    /**
     * 如果为null，则返回new ArrayList()。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    @SuppressWarnings("rawtypes")
    public static Collection<?> nullToEmpty(Collection<?> object) {
        return object == null ? new ArrayList() : object;
    }

    /**
     * 如果为null，则返回new LinkedHashMap()。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    @SuppressWarnings("rawtypes")
    public static Map<?, ?> nullToEmpty(Map<?, ?> object) {
        return object == null ? new LinkedHashMap() : object;
    }

    /**
     * 如果为null，则返回new Object[0]。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static Object[] nullToEmpty(Object[] object) {
        return object == null ? new Object[0] : object;
    }

    /**
     * 如果为null，则返回new boolean[0]。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static boolean[] nullToEmpty(boolean[] object) {
        return object == null ? new boolean[0] : object;
    }

    /**
     * 如果为null，则返回new short[0]。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static short[] nullToEmpty(short[] object) {
        return object == null ? new short[0] : object;
    }

    /**
     * 如果为null，则返回new int[0]。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static int[] nullToEmpty(int[] object) {
        return object == null ? new int[0] : object;
    }

    /**
     * 如果为null，则返回new long[0]。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static long[] nullToEmpty(long[] object) {
        return object == null ? new long[0] : object;
    }

    /**
     * 如果为null，则返回new float[0]。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static float[] nullToEmpty(float[] object) {
        return object == null ? new float[0] : object;
    }

    /**
     * 如果为null，则返回new double[0]。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static double[] nullToEmpty(double[] object) {
        return object == null ? new double[0] : object;
    }

    /**
     * 如果为null，则返回new Boolean[0]。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static Boolean[] nullToEmpty(Boolean[] object) {
        return object == null ? new Boolean[0] : object;
    }

    /**
     * 如果为null，则返回new Short[0]。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static Short[] nullToEmpty(Short[] object) {
        return object == null ? new Short[0] : object;
    }

    /**
     * 如果为null，则返回new Integer[0]。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static Integer[] nullToEmpty(Integer[] object) {
        return object == null ? new Integer[0] : object;
    }

    /**
     * 如果为null，则返回new Long[0]。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static Long[] nullToEmpty(Long[] object) {
        return object == null ? new Long[0] : object;
    }

    /**
     * 如果为null，则返回new Float[0]。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static Float[] nullToEmpty(Float[] object) {
        return object == null ? new Float[0] : object;
    }

    /**
     * 如果为null，则返回new Double[0]。
     *
     * @param object 对象
     * @return 对象
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static Double[] nullToEmpty(Double[] object) {
        return object == null ? new Double[0] : object;
    }

    /**
     * 测试是否不为空，不为空情况：非null，非空字符串，非空列表，非空Map。
     *
     * @param object 对象
     * @return true if is not empty, else return false.
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since Mar 4, 2018
     */
    public static boolean isNotEmpty(Object object) {
        return !isEmpty(object);
    }

    /**
     * 测试两个是否相等，使用equals判断，相等则返回 NULL ，否则返回原对象。
     *
     * @param object 对象
     * @param equal  对象
     * @param <T>    对象类型
     * @return null if is equal, else return object.
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since 2018-06-29
     */
    public static <T> T equalsToNull(T object, T equal) {
        if (equal == null) {
            return object;
        } else if (object == null) {
            return null;
        } else {
            return (T) (object.equals(equal) ? null : object);
        }
    }

    public static Integer toInt(String value) {
        return toInt(value, null);
    }

    public static Integer toInt(String value, Integer defaultValue) {
        try {
            return Integer.valueOf(value);
        } catch (Exception e) {
        }
        return defaultValue;
    }

    public static Long toLong(String value) {
        return toLong(value, null);
    }

    public static Long toLong(String value, Long defaultValue) {
        try {
            return Long.valueOf(value);
        } catch (Exception e) {
        }
        return defaultValue;
    }

    public static Double toDouble(String value) {
        return toDouble(value, null);
    }

    public static Double toDouble(String value, Double defaultValue) {
        try {
            return Double.valueOf(value);
        } catch (Exception e) {
        }
        return defaultValue;
    }

    public static void download(String fileName, InputStream is, HttpServletRequest request,
                                HttpServletResponse response) throws Exception {
        // 先去掉文件名称中的空格,然后转换编码格式为utf-8,保证不出现乱码,这个文件名称用于浏览器的下载框中自动显示的文件名
        fileName = StringUtils.replace(fileName, " ", "");
        response.reset();
        response.setStatus(200);
        response.addHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodeUrl(fileName));
        response.setContentType("application/octet-stream; charset=utf-8");
        IOUtils.copyLarge(is, response.getOutputStream());
        response.getOutputStream().flush();
    }

    public static String mapToParamString(Map<?, ?> map) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            sb.append(encodeUrl(entry.getKey())).append("=")
                    .append(entry.getValue() == null ? "" : encodeUrl(entry.getValue())).append("&");
        }
        if (sb.length() > 0) {
            sb.setLength(sb.length() - 1);
        }
        return sb.toString();
    }

    public static String encodeUrl(Object value) {
        try {
            return URLEncoder.encode(String.valueOf(value), "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return String.valueOf(value);
    }

    public static String uuid() {
        return UUID.randomUUID().toString();
    }

    /**
     * 计算字符串长度，中文（包括日韩中文）算两个字符，如：[你好，世界,123]，输出为：[14]
     *
     * @param str
     * @return
     */
    public static int lenChinese(String str) {
        int start = '\u2e80';
        int len = 0;
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) >= start) {
                len++;
            }
            len++;
        }
        return len;
    }

    public static Class<?> loadClass(String className) {
        try {
            return Class.forName(className);
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * 慢反射调用，只适用于只调用一次的情况。适合方法没有重载的情况。
     */
    public static <T> T slowInvoke(Object obj, String methodName, Object[] args, AtomicBoolean status) {
        if (obj == null) {
            status.set(false);
            return null;
        }
        args = args == null ? new Object[0] : args;
        int argsLen = args.length;
        List<Method> methods = Utils.filter(Utils.toList(obj.getClass().getMethods()), method ->
                method.getName().equals(methodName) && method.getParameterCount() == argsLen
        );
        for (Method method : methods) {
            try {
                T result = (T) method.invoke(obj, args);
                status.set(true);
                return result;
            } catch (Exception e) {
                status.set(false);
            }
        }
        status.set(false);
        return null;
    }

    /**
     * 慢反射调用，只适用于只调用一次的情况。适合方法没有重载的情况。
     */
    public static <T> T slowNewInstance(String className, Object[] args) {
        try {
            args = args == null ? new Object[0] : args;
            int argsLen = args.length;
            Class<?> cls = Class.forName(className);
            for (Constructor<?> constructor : cls.getConstructors()) {
                if (constructor.getParameterCount() == argsLen) {
                    try {
                        return (T) constructor.newInstance(args);
                    } catch (Exception e) {
                    }
                }
            }
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * 替换匹配的英文单词
     *
     * @param str     目标字符串
     * @param word    英文单词
     * @param replace 替换内容
     * @return 替换后的结果
     */
    public static String replaceWord(String str, String word, String replace) {
        int index = 0, lastPos = 0;
        StringBuilder sb = new StringBuilder(str.length() * 2);
        while ((index = indexWord(str, word, index)) > -1) {
            sb.append(str.substring(lastPos, index)).append(replace);
            lastPos = index + word.length();
            index = lastPos;
        }
        if (lastPos == 0) {
            return str;
        }
        return sb.append(str.substring(lastPos)).toString();
    }

    /**
     * 是否匹配英文单词
     *
     * @param str  目标字符串
     * @param word 英文单词
     * @return false:没有匹配，true:匹配位置
     */
    public static boolean containsWord(String str, String word) {
        return indexWord(str, word) > -1;
    }

    /**
     * 匹配英文单词
     *
     * @param str  目标字符串
     * @param word 英文单词
     * @return -1:没有匹配，>=0:匹配位置
     */
    public static int indexWord(String str, String word) {
        return indexWord(str, word, 0);
    }

    /**
     * 匹配英文单词
     *
     * @param str       目标字符串
     * @param word      英文单词
     * @param fromIndex 起始位置
     * @return -1:没有匹配，>=0:匹配位置
     */
    public static int indexWord(String str, String word, int fromIndex) {
        if (word == null || word.length() < 1 || str == null || str.length() < 1) {
            return -1;
        }
        boolean isSplitChar = true;
        int len0 = str.length(), len1 = word.length();
        char[] char0 = str.toCharArray(), char1 = word.toCharArray();
        for (int i = fromIndex; i < len0; i++) {
            char c = char0[i];
            if (isSplitChar && c == char1[0] && i + len1 <= len0) {
                boolean hasMatch = true;
                for (int j = 1; j < len1; j++) {
                    if (char0[i + j] != char1[j]) {
                        hasMatch = false;
                        break;
                    }
                }
                if (hasMatch) {
                    if (i + len1 == len0) {
                        return i;
                    } else if (!isCharOrNum(char0[i + len1])) {
                        return i;
                    }
                }
            } else if (!isCharOrNum(c)) {
                isSplitChar = true;
            } else {
                isSplitChar = false;
            }
        }
        return -1;
    }

    /**
     * 是否为字母或数字：a-z, A-Z, 0-9
     */
    public static boolean isCharOrNum(char c) {
        return c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c >= 'a' && c <= 'a';
    }

    /**
     * 编码CSV值，把双引号替换成两个双引号
     *
     * @param text
     * @return
     */
    public static String encodeCsvValue(String text) {
        if (text == null) {
            return null;
        }
        if (text.length() < 1) {
            return "";
        }
        int commaIndex = text.indexOf(',');
        int quoteIndex = text.indexOf('"');
        int returnIndex = text.indexOf('\n');
        if (commaIndex < 0 && quoteIndex < 0 && returnIndex < 0) {
            return text;
        }
        if (quoteIndex > -1) {
            text = text.replaceAll("\"", "\"\"");
        }
        return "\"" + text + "\"";
    }

    /**
     * 解码CSV值，把两个双引号替换成一个双引号
     *
     * @param text
     * @return
     */
    public static String decodeCsvValue(String text) {
        if (text == null) {
            return null;
        }
        if (text.length() < 1) {
            return "";
        }
        StringBuilder sb = new StringBuilder(text.length());
        for (int i = 0; i < text.length(); i++) {
            char c = text.charAt(i);
            if (c == '"') {
                char n = i + 1 < text.length() ? text.charAt(i + 1) : ' ';
                if (n == '"') {
                    sb.append(c);
                    i++;
                }
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
}
